// Xampler, a MIDI-driven sampler
// Copyright (C) 2006 Nick Thomas
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
// 02110-1301 USA

#include "State.hpp"
#include <sstream>
#include <string.h>
#include <fstream>
using namespace xampler;

State *xampler::g_state;
const char *SAVE_FILE_MAGIC = "XAMPLER-R1";

State::State()
{
    for (int i = 0; i < MAX_INSTRUMENTS; i++) {
        // XXX: This is a *really* stupid way to construct the name.
        std::ostringstream tmp_name;
        tmp_name << "Instrument " << i + 1;
        Glib::ustring name(tmp_name.str());

        m_instruments[i].set_name(name);
        m_instruments[i].set_number(i);
    }
}

// We use a simple plain text file for the save file. It begins with a
// magic string, "XAMPLER-R1." This is followed by the names of all of
// the instruments, one to a line. Following these, the samples are
// each described by a pair of lines in the following format:
//
// <name>
// <instrument number> <sample number> <volume> <balance> <filename>
//
// Samples which do not contain data are not saved.
bool State::save(Glib::ustring filename, Glib::ustring &error)
{
    bool err = false;

    // Open the file.
    std::ofstream file;
    file.open(filename.c_str());
    if (file.fail()) {
        // XXX: Can we improve this error message?
        error = "Could not open the save file.";
        err = true;
    }

    // Write the data.
    if (!err) {
        file << SAVE_FILE_MAGIC << "\n";

        for (int i = 0; i < MAX_INSTRUMENTS; i++) {
            file << m_instruments[i].get_name() << "\n";
        }

        for (int instrument = 0; instrument < MAX_INSTRUMENTS; instrument++) {
            for (int sample = 0; sample < MAX_SAMPLES; sample++) {
                Sample &sample_data =
                    m_instruments[instrument].get_sample(sample);

                if (sample_data.is_empty()) {
                    continue;
                }

                file << sample_data.get_name() << "\n"
                     << instrument << " "
                     << sample << " "
                     << sample_data.get_volume() << " "
                     << sample_data.get_balance() << " "
                     << sample_data.get_filename() << "\n";
            }
        }

        file.flush();

        if (file.fail()) {
            error = "Write error.";
            err = true;
            file.close();
        }
    }

    // Close the file.
    if (!err) {
        file.close();

        if (file.fail()) {
            error = "Write error.";
            err = true;
        }
    }

    return !err;
}

// This function is currently quite picky about syntax, and doesn't
// really do any error checking. Probably not a problem, since we only
// expect to be reading files written by the above function. But, it's
// worth mentioning.
bool State::load(Glib::ustring filename, Glib::ustring &error)
{
    bool err = false;

    std::ifstream file;
    file.open(filename.c_str());
    if (file.fail()) {
        // XXX: Can we improve this error message?
        error = "Could not open the file.";
        err = true;
    }

    // Check for the magic string.
    if (!err) {
        char buf[256];

        file.getline(buf, sizeof(buf));

        if (file.fail()) {
            error = "Read error at start of file.";
            err = true;
        } else if (strcmp(buf, SAVE_FILE_MAGIC)) {
            error = "File is not a Xampler save file.";
            err = true;
        }
    }

    // Read the data.
    if (!err) {
        for (int i = 0; i < MAX_INSTRUMENTS; i++) {
            // XXX: Might want to remove the buffer size limitation
            // here.
            char buf[256];
            file.getline(buf, sizeof(buf));
            m_instruments[i].set_name(buf);
        }

        if (file.fail()) {
            error = "Read error while reading instrument records.";
            err = true;
        }
    }

    while (!err && file.good()) {
        char name_buf[256], filename_buf[256];
        int instrument, sample;
        double volume, balance;

        file.getline(name_buf, sizeof(name_buf));
        if (!file.good()) break;
        file >> instrument >> sample >> volume >> balance;
        if (!file.good()) break;
        file.ignore(1);
        if (!file.good()) break;
        file.getline(filename_buf, sizeof(filename_buf));
        if (!file.good()) break;

        Glib::ustring name, filename;
        name.assign(name_buf);
        filename.assign(filename_buf);

        Sample &sample_data =
            m_instruments[instrument].get_sample(sample);
        sample_data.set_name(name);
        sample_data.set_volume(volume);
        sample_data.set_balance(balance);

        Glib::ustring sample_load_error;
        if (!sample_data.load_data(filename, sample_load_error)) {
            std::ostringstream error_str;
            error_str << "Error loading sample '" << filename << "': "
                      << sample_load_error;
            error = error_str.str();
            err = true;
        }
    }

    if (!err && file.bad()) {
        error = "Read error while reading sample records.";
        err = true;
    }

    // Close the file.
    if (!err) {
        file.close();

        if (file.bad()) {
            error = "Can this happen?";
            err = true;
        }
    }

    return !err;
}
